using System; using UnityEngine; namespace UnityEditor.SettingsManagement { [Flags] enum SettingVisibility { None = 0 << 0, /// <summary> /// Matches any static field implementing IUserSetting and tagged with [UserSettingAttribute(visibleInSettingsProvider = true)]. /// These fields are automatically scraped by the SettingsProvider and displayed. /// </summary> Visible = 1 << 0, /// <summary> /// Matches any static field implementing IUserSetting and tagged with [UserSettingAttribute(visibleInSettingsProvider = false)]. /// These fields will be reset by the "Reset All" menu in SettingsProvider, but are not shown in the interface. /// Typically these fields require some conditional formatting or data handling, and are shown in the /// SettingsProvider UI with a [UserSettingBlockAttribute]. /// </summary> Hidden = 1 << 1, /// <summary> /// A static or instance field tagged with [SettingsKeyAttribute]. /// Unlisted settings are not shown in the SettingsProvider, but are reset to default values by the "Reset All" /// context menu. /// </summary> Unlisted = 1 << 2, /// <summary> /// A static field implementing IUserSetting that is not marked with any setting attribute. /// Unregistered IUserSetting fields are not affected by the SettingsProvider. /// </summary> Unregistered = 1 << 3, All = Visible | Hidden | Unlisted | Unregistered } /// <summary> /// An interface that represents a user setting. /// Types implementing IUserSetting are eligible for use with <see cref="UserSettingAttribute"/>, which enables /// fields to automatically populate the <see cref="UserSettingsProvider"/> interface. /// </summary> public interface IUserSetting { /// <summary> /// Implement this property to get the key for this value. /// </summary> /// <value>The key used to identify the settings entry. This is used along with the <see cref="type"/> to uniquely identify the value.</value> string key { get; } /// <summary> /// Implement this property to get the type of the stored value. /// </summary> /// <value>The type of value. This is used along with the <see cref="key"/> to uniquely identify the value.</value> Type type { get; } /// <summary> /// Implement this property to get the location in the UI where this setting will appear. /// </summary> /// <value> /// Indicates whether this is a <see cref="UnityEditor.SettingsScope.Project"/> setting /// or a <see cref="UnityEditor.SettingsScope.User"/> preference. /// </value> SettingsScope scope { get; } /// <summary> /// Implement this property to get the name of the <see cref="ISettingsRepository"/> that this setting should be associated with. /// If null, the first repository matching the <see cref="scope"/> is used. /// </summary> /// <value>The bare filename of this repository.</value> string settingsRepositoryName { get; } /// <summary> /// Implement this property to get the <see cref="Settings"/> instance to save and load this setting from. /// </summary> /// <value>A reference to <see cref="Settings"/> instance.</value> Settings settings { get; } /// <summary> /// Implement this method to return the stored settings value. /// If you are implementing IUserSetting, you should cache this value. /// </summary> /// <returns> /// The stored value. /// </returns> object GetValue(); /// <summary> /// Implement this method to return the the default value for this setting. /// </summary> /// <returns> /// The default value for this setting. /// </returns> object GetDefaultValue(); /// <summary> /// Implement this method to set the value for this setting. /// </summary> /// <param name="value">The new value.</param> /// <param name="saveProjectSettingsImmediately"> /// True to immediately serialize the <see cref="ISettingsRepository"/> that is backing this value; or false to postpone. /// If not serializing immediately, be sure to call <see cref="Settings.Save"/>. /// </param> void SetValue(object value, bool saveProjectSettingsImmediately = false); /// <summary> /// Implement this method to explicitly update the <see cref="ISettingsRepository"/> that is backing this value. /// When the inspected type is a reference value, it is possible to change properties without affecting the /// backing setting. ApplyModifiedProperties provides a method to force serialize these changes. /// </summary> void ApplyModifiedProperties(); /// <summary> /// Implement this method to set the current value back to the default. /// </summary> /// <param name="saveProjectSettingsImmediately">True to immediately re-serialize project settings. By default, no values are updated. </param> void Reset(bool saveProjectSettingsImmediately = false); /// <summary> /// Implement this method to delete the saved setting. This does not clear the current value. /// </summary> /// <seealso cref="Reset"/> /// <param name="saveProjectSettingsImmediately">True to immediately re-serialize project settings. By default, no values are updated.</param> void Delete(bool saveProjectSettingsImmediately = false); } /// <summary> /// A generic implementation of <see cref="IUserSetting"/> to use with a <see cref="Settings"/> instance. This default /// implementation assumes that the <see cref="Settings"/> instance contains two <see cref="ISettingsRepository"/> interfaces: /// - Project settings (<see cref="SettingsScope.Project"/>) /// - User preferences (<see cref="SettingsScope.User"/>) /// </summary> /// <typeparam name="T">Type of value.</typeparam> public class UserSetting<T> : IUserSetting { bool m_Initialized; string m_Key; string m_Repository; T m_Value; T m_DefaultValue; SettingsScope m_Scope; Settings m_Settings; UserSetting() { } /// <summary> /// Initializes and returns an instance of the UserSetting<T> type. /// </summary> /// <param name="settings">The <see cref="Settings"/> instance to save and load this setting from.</param> /// <param name="key">The key for this value.</param> /// <param name="value">The default value for this key.</param> /// <param name="scope">The scope for this setting. By default, the scope is the project.</param> public UserSetting(Settings settings, string key, T value, SettingsScope scope = SettingsScope.Project) { m_Key = key; m_Repository = null; m_Value = value; m_Scope = scope; m_Initialized = false; m_Settings = settings; } /// <summary> /// Initializes and returns an instance of the UserSetting<T> type using the specified repository. /// </summary> /// <param name="settings">The <see cref="Settings"/> instance to save and load this setting from.</param> /// <param name="repository">The <see cref="ISettingsRepository"/> name to save and load this setting from. Specify null to save to the first available instance.</param> /// <param name="key">The key for this value.</param> /// <param name="value">The default value for this key.</param> /// <param name="scope">The scope for this setting. By default, the scope is the project.</param> public UserSetting(Settings settings, string repository, string key, T value, SettingsScope scope = SettingsScope.Project) { m_Key = key; m_Repository = repository; m_Value = value; m_Scope = scope; m_Initialized = false; m_Settings = settings; } /// <summary> /// Gets the key for this value. /// </summary> /// <seealso cref="IUserSetting.key"/> public string key { get { return m_Key; } } /// <summary> /// Gets the name of the repository that this setting is saved in. /// </summary> /// <seealso cref="IUserSetting.settingsRepositoryName" /> public string settingsRepositoryName { get { return m_Repository; } } /// <summary> /// Gets the type that this setting represents (<T>). /// </summary> /// <seealso cref="IUserSetting.type" /> public Type type { get { return typeof(T); } } /// <summary> /// Returns a copy of the default value. /// </summary> /// <returns> /// The default value. /// </returns> /// <seealso cref="IUserSetting.GetDefaultValue" /> public object GetDefaultValue() { return defaultValue; } /// <summary> /// Returns the currently stored value. /// </summary> /// <returns> /// The value that is currently set. /// </returns> /// <seealso cref="IUserSetting.GetValue" /> public object GetValue() { return value; } /// <summary> /// Gets the scope (<see cref="ISettingsRepository"/>) where the <see cref="Settings"/> instance saves /// its data. /// </summary> /// <seealso cref="IUserSetting.scope" /> public SettingsScope scope { get { return m_Scope; } } /// <summary> /// Gets the <see cref="Settings"/> instance to read from and save to. /// </summary> /// <seealso cref="IUserSetting.settings" /> public Settings settings { get { return m_Settings; } } /// <summary> /// Sets the value for this setting from the specified object. /// </summary> /// <param name="value">The new value to set.</param> /// <param name="saveProjectSettingsImmediately"> /// Set this value to true if you want to immediately serialize the <see cref="ISettingsRepository"/> /// that is backing this value. By default, this is false. /// /// **Note**: If not serializing immediately, you need to call <see cref="Settings.Save"/>. /// </param> /// <seealso cref="IUserSetting.SetValue" /> public void SetValue(object value, bool saveProjectSettingsImmediately = false) { // we do want to allow null values if (value != null && !(value is T)) throw new ArgumentException("Value must be of type " + typeof(T) + "\n" + key + " expecting value of type " + type + ", received " + value.GetType()); SetValue((T)value, saveProjectSettingsImmediately); } /// <inheritdoc cref="SetValue" /> public void SetValue(T value, bool saveProjectSettingsImmediately = false) { Init(); m_Value = value; settings.Set<T>(key, m_Value, m_Scope); if (saveProjectSettingsImmediately) settings.Save(); } /// <summary> /// Deletes the saved setting but doesn't clear the current value. /// </summary> /// <param name="saveProjectSettingsImmediately"> /// Set this value to true if you want to immediately serialize the <see cref="ISettingsRepository"/> /// that is backing this value. By default, this is false. /// /// **Note**: If not serializing immediately, you need to call <see cref="Settings.Save"/>. /// </param> /// <seealso cref="Reset" /> /// <seealso cref="IUserSetting.Delete"/> public void Delete(bool saveProjectSettingsImmediately = false) { settings.DeleteKey<T>(key, scope); // Don't Init() because that will set the key again. We just want to reset the m_Value with default and // pretend that this field hasn't been initialised yet. m_Value = ValueWrapper<T>.DeepCopy(m_DefaultValue); m_Initialized = false; } /// <summary> /// Forces Unity to serialize the changed properties to the <see cref="ISettingsRepository"/> that is backing this value. /// When the inspected type is a reference value, it is possible to change properties without affecting the /// backing setting. /// </summary> /// <seealso cref="IUserSetting.ApplyModifiedProperties"/> public void ApplyModifiedProperties() { settings.Set<T>(key, m_Value, m_Scope); settings.Save(); } /// <summary> /// Sets the current value back to the default. /// </summary> /// <param name="saveProjectSettingsImmediately"> /// Set this value to true if you want to immediately serialize the <see cref="ISettingsRepository"/> /// that is backing this value. By default, this is false. /// /// **Note**: If not serializing immediately, you need to call <see cref="Settings.Save"/>. /// </param> /// <seealso cref="IUserSetting.Reset"/> public void Reset(bool saveProjectSettingsImmediately = false) { SetValue(defaultValue, saveProjectSettingsImmediately); } void Init() { if (!m_Initialized) { if (m_Scope == SettingsScope.Project && settings == null) throw new Exception("UserSetting \"" + m_Key + "\" is attempting to access SettingsScope.Project setting with no Settings instance!"); m_Initialized = true; // DeepCopy uses EditorJsonUtility which is not permitted during construction m_DefaultValue = ValueWrapper<T>.DeepCopy(m_Value); if (settings.ContainsKey<T>(m_Key, m_Scope)) m_Value = settings.Get<T>(m_Key, m_Scope); else settings.Set<T>(m_Key, m_Value, m_Scope); } } /// <summary> /// Gets the default value for this setting. /// </summary> public T defaultValue { get { Init(); return ValueWrapper<T>.DeepCopy(m_DefaultValue); } } /// <summary> /// Gets or sets the currently stored value. /// </summary> public T value { get { Init(); return m_Value; } set { SetValue(value); } } /// <summary> /// Implicit casts this setting to the backing type `T`. /// </summary> /// <param name="pref">The UserSetting<T> to cast to `T`.</param> /// <returns> /// The currently stored <see cref="value"/>. /// </returns> public static implicit operator T(UserSetting<T> pref) { return pref.value; } /// <summary> /// Returns a string representation of this setting. /// </summary> /// <returns>A string summary of this setting of format "[scope] setting. Key: [key] Value: [value]".</returns> public override string ToString() { return string.Format("{0} setting. Key: {1} Value: {2}", scope, key, value); } } }